//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<input.h>
///	@path	~/src/gui/input/
///	@date	2007/09/23
///	@desc	Input base.

#pragma once

#include "xeres.h"

#include "lib/math/pair.h"
#include "lib/utilities/string.h"

namespace xeres {

	// forward declaration
	class KeyInput;
	class MouseInput;

	/*!
		\enum	HandleInput
		\brief	Return value to indicate if input is handled.
	*/
	enum HandleInput
	{
		INPUT_UNHANDLED = 0 ,
		INPUT_HANDLED = 1 ,
	};

	/*!
		\class	Input
		\brief	Input base structure. It must be either keyboard input or mouse input.
	*/
	class Input
	{
	public:

		// Input type enumeration.
		enum Type
		{
			// Empty input, used to indicate non-set input structure.
			TYPE_NONE , 
			// Keyboard input.
			TYPE_KEY ,
			// Mouse input.
			TYPE_MOUSE ,
			// end
			TYPE_last
		};

		// Input action
		enum Action
		{
			ACTION_NONE				= 0 ,
			// Keyboard actions.
			ACTION_KEY_PRESS		= 1 ,
			ACTION_KEY_RELEASE		= 2 ,
			// Mouse actions.
			ACTION_MOUSE_LB_CLICK	= 3 ,
			ACTION_MOUSE_MB_CLICK	= 4 ,
			ACTION_MOUSE_RB_CLICK	= 5 ,
			ACTION_MOUSE_LB_UP		= 6 ,
			ACTION_MOUSE_MB_UP		= 7 ,
			ACTION_MOUSE_RB_UP		= 8 ,
			ACTION_MOUSE_LB_DBCLICK = 9 ,
			ACTION_MOUSE_RB_DBCLICK = 10,
			ACTION_MOUSE_WHEEL_UP	= 11,
			ACTION_MOUSE_WHEEL_DOWN = 12,
			ACTION_MOUSE_MOVE		= 13,
			// end
			ACTION_last
		};

		// States will be combined and store into state tag.
		static const uint32 INPUT_STATE_LSHIFT	= 1 << 0;
		static const uint32 INPUT_STATE_RSHIFT	= 1 << 1;
		static const uint32 INPUT_STATE_LCTRL	= 1 << 2;
		static const uint32 INPUT_STATE_RCTRL	= 1 << 3;
		static const uint32 INPUT_STATE_LALT	= 1 << 4;
		static const uint32 INPUT_STATE_RALT	= 1 << 5;

		static const uint32 INPUT_STATE_SHIFT	= INPUT_STATE_LSHIFT | INPUT_STATE_RSHIFT;
		static const uint32 INPUT_STATE_CTRL	= INPUT_STATE_LCTRL | INPUT_STATE_RCTRL;
		static const uint32 INPUT_STATE_ALT		= INPUT_STATE_LALT | INPUT_STATE_RALT;

		// Toggle states.
		static const uint32 INPUT_STATE_CAPSLOCK	= 1 << 6;
		static const uint32 INPUT_STATE_NUMLOCK		= 1 << 7;
		static const uint32 INPUT_STATE_SCRLOCK		= 1 << 8;

		// Repeat input state.
		static const uint32 INPUT_STATE_KEY_REPEAT	= 1 << 9;

		// Key combiner.
		static const uint32 INPUT_STATE_KEY_COMBINER =
			INPUT_STATE_SHIFT | INPUT_STATE_CTRL | INPUT_STATE_ALT;

		// All key states.
		static const uint32 INPUT_STATE_KEY =
			INPUT_STATE_SHIFT | INPUT_STATE_CTRL | INPUT_STATE_ALT |
			INPUT_STATE_CAPSLOCK | INPUT_STATE_NUMLOCK | INPUT_STATE_SCRLOCK;
//			INPUT_STATE_KEY_REPEAT;

		// Indicate mouse states.
		static const uint32 INPUT_STATE_LB_DOWN		= 1 << 10;
		static const uint32 INPUT_STATE_MB_DOWN		= 1 << 11;
		static const uint32 INPUT_STATE_RB_DOWN 	= 1 << 12;

		// Tag to check if any mouse button down.
		static const uint32 INPUT_STATE_MOUSE =
			INPUT_STATE_LB_DOWN | INPUT_STATE_MB_DOWN | INPUT_STATE_RB_DOWN;

		// Combiner of all input states.
		static const uint32 INPUT_STATE_ALL =
			INPUT_STATE_KEY | INPUT_STATE_MOUSE;

		/// \ctor
		Input( Type inputType , Action action , uint32 inputState )
			: m_type( inputType )
			, m_action( action )
			, m_state( inputState )
			, m_key( 0xff )
			, m_x( -1 )
			, m_y( -1 )
		{
			DEBUG_RUN( Validate() );
		}

		/// \ctor_copy
		Input( const Input& rhs )
			: m_type( rhs.m_type )
			, m_action( rhs.m_action )
			, m_state( rhs.m_state )
			, m_key( rhs.m_key )
			, m_x( rhs.m_x )
			, m_y( rhs.m_y )
		{
			DEBUG_RUN( Validate() );
		}

		/// \brief	Get type of input.
		Type GetType( void ) const { return m_type; }

		/// \brief	Get action of input.
		Action GetAction( void ) const { return m_action; }

		/// \brief	Get state of input.
		uint32 GetState( void ) const { return m_state; }

		/// \brief	Check if ALT is down.
		bool IsAltDown( void ) const { return (m_state & INPUT_STATE_ALT) != 0; }

		/// \brief	Check if Shift is down.
		bool IsShiftDown( void ) const { return (m_state & INPUT_STATE_SHIFT) != 0; }

		/// \brief	Check if CTRL is down.
		bool IsCtrlDown( void ) const { return (m_state & INPUT_STATE_CTRL) != 0; }

		///	\brief	Check CAPS lock toggle.
		bool IsCapsLocked( void ) const { return (m_state & INPUT_STATE_CAPSLOCK) != 0; }

		/// \brief	Check NUM lock toggle.
		bool IsNumLocked( void ) const { return (m_state & INPUT_STATE_NUMLOCK) != 0; }

		/// \brief	Check SCR lock toggle.
		bool IsScrLocked( void ) const { return (m_state & INPUT_STATE_SCRLOCK) != 0; }

		/// \brief	Check if left button is down.
		bool IsLeftBtnDown( void ) const { return (m_state & INPUT_STATE_LB_DOWN) != 0; }

		/// \brief	Check if middle button is down.
		bool IsMiddleBtnDown( void ) const { return (m_state & INPUT_STATE_MB_DOWN) != 0; }

		/// \brief	Check if right button is down.
		bool IsRightBtnDown( void ) const { return (m_state & INPUT_STATE_RB_DOWN) != 0; }

		///	\briec	Check if any button is down.
		bool IsAnyBtnDown( void ) const { return (m_state & INPUT_STATE_MOUSE) != 0; }

		///	\brief	Check if current key input is repeating.
		bool IsKeyRepeat( void ) const { return (m_state & INPUT_STATE_KEY_REPEAT) != 0; }

		/// \brief Determine if is KeyInput.
		bool IsKeyInput( void ) const { return GetType() == Input::TYPE_KEY; }

		/// \brief Determine if is MouseInput.
		bool IsMouseInput( void ) const { return GetType() == Input::TYPE_MOUSE; }

		/// \brief Get key input.
		const KeyInput& GetKeyInput( void ) const {
			assert( IsKeyInput() );
			return *reinterpret_cast<const KeyInput*>(this);
		}

		/// \brief Get mouse input.
		const MouseInput& GetMouseInput( void ) const {
			assert( IsMouseInput() );
			return *reinterpret_cast<const MouseInput*>(this);
		}


#ifdef _DEBUG
		/// \brief	Validate input structure.
		void Validate( void ) const;
#endif /*_DEBUG*/

	private:

	//@ data
		Type		m_type : 16;
		Action		m_action : 16;
		uint32		m_state;

	protected:
		// Input data, (Key in KeyInput, (x,y) in MouseInput)
		uint32		m_key;
		short		m_x , m_y;
	};


	/*!
		\class	KeyInput
		\brief	Keyboard input structure.
	*/
	class KeyInput : public Input
	{
	public:

		// Available key enumeration.
		enum Key
		{
			KEY_NONE			= 0x00,

			// Keyboard Buttons.
			KEY_MINIMUM_KEY		= 0x01,

			KEY_ESCAPE			= 0x01,
			KEY_1				= 0x02,
			KEY_2               = 0x03,
			KEY_3               = 0x04,
			KEY_4               = 0x05,
			KEY_5               = 0x06,
			KEY_6               = 0x07,
			KEY_7               = 0x08,
			KEY_8               = 0x09,
			KEY_9               = 0x0A,
			KEY_0               = 0x0B,
			KEY_MINUS           = 0x0C,    /* - on main keyboard */
			KEY_EQUAL           = 0x0D,
			KEY_BACKSPACE       = 0x0E,    /* backspace */
			KEY_TAB             = 0x0F,
			KEY_Q               = 0x10,
			KEY_W               = 0x11,
			KEY_E               = 0x12,
			KEY_R               = 0x13,
			KEY_T               = 0x14,
			KEY_Y               = 0x15,
			KEY_U               = 0x16,
			KEY_I               = 0x17,
			KEY_O               = 0x18,
			KEY_P               = 0x19,
			KEY_LBRACKET        = 0x1A,
			KEY_RBRACKET        = 0x1B,
			KEY_RETURN          = 0x1C,    /* Enter on main keyboard */
			KEY_LCTRL	        = 0x1D,
			KEY_A               = 0x1E,
			KEY_S               = 0x1F,
			KEY_D               = 0x20,
			KEY_F               = 0x21,
			KEY_G               = 0x22,
			KEY_H               = 0x23,
			KEY_J               = 0x24,
			KEY_K               = 0x25,
			KEY_L               = 0x26,
			KEY_SEMICOLON       = 0x27,
			KEY_APOSTROPHE      = 0x28,
			KEY_GRAVE           = 0x29,    /* accent grave */
			KEY_LSHIFT          = 0x2A,
			KEY_BACKSLASH       = 0x2B,
			KEY_Z               = 0x2C,
			KEY_X               = 0x2D,
			KEY_C               = 0x2E,
			KEY_V               = 0x2F,
			KEY_B               = 0x30,
			KEY_N               = 0x31,
			KEY_M               = 0x32,
			KEY_COMMA           = 0x33,
			KEY_PERIOD          = 0x34,    /* . on main keyboard */
			KEY_SLASH           = 0x35,    /* / on main keyboard */
			KEY_RSHIFT          = 0x36,
			KEY_NUMPADSTAR      = 0x37,    /* * on numeric keypad */
			KEY_LALT            = 0x38,    /* left Alt */
			KEY_SPACE           = 0x39,
			KEY_CAPSLOCK        = 0x3A,
			KEY_F1              = 0x3B,
			KEY_F2              = 0x3C,
			KEY_F3              = 0x3D,
			KEY_F4              = 0x3E,
			KEY_F5              = 0x3F,
			KEY_F6              = 0x40,
			KEY_F7              = 0x41,
			KEY_F8              = 0x42,
			KEY_F9              = 0x43,
			KEY_F10             = 0x44,
			KEY_PAUSE           = 0x45,
			KEY_SCRLOCK         = 0x46,    /* Scroll Lock */
			KEY_NUMPAD7         = 0x47,
			KEY_NUMPAD8         = 0x48,
			KEY_NUMPAD9         = 0x49,
			KEY_NUMPADMINUS     = 0x4A,    /* - on numeric keypad */
			KEY_NUMPAD4         = 0x4B,
			KEY_NUMPAD5         = 0x4C,
			KEY_NUMPAD6         = 0x4D,
			KEY_NUMPADADD       = 0x4E,    /* + on numeric keypad */
			KEY_NUMPAD1         = 0x4F,
			KEY_NUMPAD2         = 0x50,
			KEY_NUMPAD3         = 0x51,
			KEY_NUMPAD0         = 0x52,
			KEY_NUMPADPERIOD    = 0x53,    /* . on numeric keypad */
			KEY_OEM_102         = 0x56,    /* < > | on UK/Germany keyboards */
			KEY_F11             = 0x57,
			KEY_F12             = 0x58,

			KEY_F13             = 0x64,    /*                     (NEC PC98) */
			KEY_F14             = 0x65,    /*                     (NEC PC98) */
			KEY_F15             = 0x66,    /*                     (NEC PC98) */

			KEY_KANA            = 0x70,    /* (Japanese keyboard)            */
			KEY_ABNT_C1         = 0x73,    /* / ? on Portuguese (Brazilian) keyboards */
			KEY_CONVERT         = 0x79,    /* (Japanese keyboard)            */
			KEY_NOCONVERT       = 0x7B,    /* (Japanese keyboard)            */
			KEY_YEN             = 0x7D,    /* (Japanese keyboard)            */
			KEY_ABNT_C2         = 0x7E,    /* Numpad . on Portuguese (Brazilian) keyboards */
			KEY_NUMPADEQUALS    = 0x8D,    /* = on numeric keypad (NEC PC98) */
			KEY_PREVTRACK       = 0x90,    /* Previous Track (DIK_CIRCUMFLEX on Japanese keyboard) */
			KEY_AT              = 0x91,    /*                     (NEC PC98) */
			KEY_COLON           = 0x92,    /*                     (NEC PC98) */
			KEY_UNDERLINE       = 0x93,    /*                     (NEC PC98) */
			KEY_KANJI           = 0x94,    /* (Japanese keyboard)            */
			KEY_STOP            = 0x95,    /*                     (NEC PC98) */
			KEY_AX              = 0x96,    /*                     (Japan AX) */
			KEY_UNLABELED       = 0x97,    /*                        (J3100) */
			KEY_NEXTTRACK       = 0x99,    /* Next Track */
			KEY_NUMPADENTER     = 0x9C,    /* Enter on numeric keypad */
			KEY_RCTRL	        = 0x9D,
			KEY_MUTE            = 0xA0,    /* Mute */
			KEY_CALCULATOR      = 0xA1,    /* Calculator */
			KEY_PLAYPAUSE       = 0xA2,    /* Play / Pause */
			KEY_MEDIASTOP       = 0xA4,    /* Media Stop */
			KEY_VOLUMEDOWN      = 0xAE,    /* Volume - */
			KEY_VOLUMEUP        = 0xB0,    /* Volume + */
			KEY_WEBHOME         = 0xB2,    /* Web home */
			KEY_NUMPADCOMMA     = 0xB3,    /* , on numeric keypad (NEC PC98) */
			KEY_NUMPADSLASH     = 0xB5,    /* / on numeric keypad */
			KEY_SYSRQ           = 0xB7,
			KEY_RALT            = 0xB8,    /* right Alt */
			KEY_NUMLOCK         = 0xC5,    /* Pause */
			KEY_HOME            = 0xC7,    /* Home on arrow keypad */
			KEY_UPARROW         = 0xC8,    /* UpArrow on arrow keypad */
			KEY_PGUP            = 0xC9,    /* PgUp on arrow keypad */
			KEY_LEFTARROW       = 0xCB,    /* LeftArrow on arrow keypad */
			KEY_RIGHTARROW      = 0xCD,    /* RightArrow on arrow keypad */
			KEY_END             = 0xCF,    /* End on arrow keypad */
			KEY_DOWNARROW       = 0xD0,    /* DownArrow on arrow keypad */
			KEY_PGDN            = 0xD1,    /* PgDn on arrow keypad */
			KEY_INSERT          = 0xD2,    /* Insert on arrow keypad */
			KEY_DELETE          = 0xD3,    /* Delete on arrow keypad */
			KEY_LWIN            = 0xDB,    /* Left Windows key */
			KEY_RWIN            = 0xDC,    /* Right Windows key */
			KEY_APPS            = 0xDD,    /* AppMenu key */
			KEY_POWER           = 0xDE,    /* System Power */
			KEY_SLEEP           = 0xDF,    /* System Sleep */
			KEY_WAKE            = 0xE3,    /* System Wake */
			KEY_WEBSEARCH       = 0xE5,    /* Web Search */
			KEY_WEBFAVORITES    = 0xE6,    /* Web Favorites */
			KEY_WEBREFRESH      = 0xE7,    /* Web Refresh */
			KEY_WEBSTOP         = 0xE8,    /* Web Stop */
			KEY_WEBFORWARD      = 0xE9,    /* Web Forward */
			KEY_WEBBACK         = 0xEA,    /* Web Back */
			KEY_MYCOMPUTER      = 0xEB,    /* My Computer */
			KEY_MAIL            = 0xEC,    /* Mail */
			KEY_MEDIASELECT     = 0xED,    /* Media Select */

			KEY_last			= 0xED
		};

		/// \ctor
		KeyInput( Key key , bool press , uint32 inputState )
			: Input( TYPE_KEY , press ? ACTION_KEY_PRESS : ACTION_KEY_RELEASE , inputState )
		{
			m_key = key;
		}

		/// \ctor_copy
		KeyInput( const KeyInput& rhs )
			: Input( rhs )
		{
		}

		/// \brief	Determine if key is pressed.
		bool IsPressed( void ) const { return GetAction() == ACTION_KEY_PRESS; }

		/// \brief	Determine if key is release.
		bool IsReleased( void ) const { return GetAction() == ACTION_KEY_RELEASE; }

		/// \brief	Get action key.
		Key GetKey( void ) const { return (Key)m_key; }

		/// \brief	Get input (as character).
		wchar_t GetInputChar( void ) const;

		///	\brief	Get input (as string).
		const WString& GetInputString( void ) const;

		/// \brief	Get input (as string).
		static const WString& GetInputString( Key key );
	};

	/*!
		\class	MouseInput
		\brief
	*/
	class MouseInput : public Input
	{
	public:

		/// \ctor_set
		MouseInput( int x , int y , Input::Action action , uint32 inputState )
			: Input( TYPE_MOUSE , action , inputState )
		{
			m_x = (short)x;
			m_y = (short)y;
		}

		/// \ctor_copy
		MouseInput( const MouseInput& rhs )
			: Input( rhs )
		{
		}

		/// \brief	Get mouse position(X).
		int GetX( void ) const { return m_x; }

		/// \brief	Get mouse position(Y).
		int GetY( void ) const { return m_y; }

		/// \brief	Get point.
		Pair<int> GetPoint( void ) const { return Pair<int>( m_x , m_y ); }
	};

	/*! 
		\class	InputMsg
		\brief	Carries both key & mouse input.
	*/
	class InputMsg
	{
	public:

		/// \ctor
		InputMsg( void ) { memset( m_buffer , 0 , sizeof(m_buffer) ); }

		/// \ctor
		InputMsg( const KeyInput& keyInput ) { memcpy( m_buffer , &keyInput , sizeof(KeyInput) ); }

		/// \ctor
		InputMsg( const MouseInput& mouseInput ) { memcpy( m_buffer , &mouseInput , sizeof(MouseInput) ); }

		/// \ctor_copy
		InputMsg( const InputMsg& rhs ) { memcpy( m_buffer , rhs.m_buffer , sizeof(m_buffer) ); }

		/// \brief Get general input.
		const Input& GetInput( void ) const { return *reinterpret_cast<const Input*>(this); }

		/// \brief Get key input.
		const KeyInput& GetKeyInput( void ) const { return GetInput().GetKeyInput(); }

		/// \brief Get mouse input.
		const MouseInput& GetMouseInput( void ) const { return GetInput().GetMouseInput(); }

	private:

		//@ data
		byte	m_buffer[ ALIGNOF( sizeof(Input) / sizeof(byte) , 16 ) ];
	};

	/*! 
		\class	InputTranslator
		\brief	
	*/
	class InputTranslator
	{
	public:

		/// \ctor
		InputTranslator( void );


		/// \name	Message Translation Interface.
		//@{

		/// \brief	Translate message (to a Messenger structure).
		HandleInput TranslateMessage( HWND hWnd , UINT message , WPARAM wParam , LPARAM lParam , InputMsg * msg );

		/// \brief	Call while needs to refresh states.
		void RefreshStates( HWND hWnd );

		/// \brief	Query capturing.
		bool IsCapturing( void ) const { return m_capturing; }

		/// \brief	Set auto capture while clicking mouse button.
		void SetAutoCapture( bool capture ) { m_autoCapture = capture; }

		/// \brief	Get current input state.
		uint32 GetState( void ) const { return m_inputState; }

		//@}

	protected:

		/// \brief	Process message. ( Notice if msg is not null OnKeyInput & OnMouseInput will not be called )
		HandleInput ProcessMessage( HWND hWnd , UINT message , WPARAM wParam , LPARAM lParam , InputMsg * msg );

	private:

		// internal process keyboard input
		HandleInput ProcessKeyInput( bool press , WPARAM wParam , LPARAM lParam , InputMsg * input );

		// internal process mouse input
		HandleInput ProcessMouseInput( HWND hWnd , UINT message , WPARAM wParam , LPARAM lParam , InputMsg * msg );

		//@ data

		uint32			m_inputState;
		POINTS			m_lastPos;

		bool			m_capturing;
		bool			m_autoCapture;
		bool			m_spare0[2];
	};

	/*! 
		\class	InputDispatcher
		\brief
	*/
	class InputDispatcher : public InputTranslator
	{
	public:

		/// \ctor
		InputDispatcher( void );

		/// \name	Message Dispatching Interface.
		//@{

		///	\brief	Process key input message.
		virtual HandleInput OnKeyInput( const KeyInput& input ) = 0;

		///	\brief	Process mouse input message.
		virtual HandleInput OnMouseInput( const MouseInput& input ) = 0;

		/// \brief	Dispatch message (to call proper input callback).
		HandleInput DispatchMessage( HWND hWnd , UINT message , WPARAM wParam , LPARAM lParam );

		//@}
	};

} // namespace xeres
